using System;
using System.Reflection;

namespace Fadd.Messaging
{
	/// <summary>
	/// Wraps a typed subscriber
	/// </summary>
	/// <remarks>
	/// All subscribers are generics which makes them a bit hard to work with. By wrapping
	/// them in this object we can work with them in a non-generic way.
	/// </remarks>
	public class SubscriberWrapper
	{
		private readonly MethodBase _consumeMethod;
		private readonly Type _messageType;
		private readonly object _subscriber;

		/// <summary>
		/// Initializes a new instance of the <see cref="SubscriberWrapper"/> class.
		/// </summary>
		/// <param name="messageType">Type that the subscriber is for</param>
		/// <param name="subscriber">Subscriber receiving the message.</param>
		public SubscriberWrapper(Type messageType, object subscriber)
		{
			_messageType = messageType;
			_subscriber = subscriber;
			Type subscriberType = typeof (ISubscriberOf<>).MakeGenericType(messageType);
			_consumeMethod = subscriberType.GetMethod("Consume", new[] {messageType});
		}


		/// <summary>
		/// Gets subscriber instance.
		/// </summary>
		public object Instance
		{
			get { return _subscriber; }
		}

		/// <summary>
		/// Invoke subscriber with a message
		/// </summary>
		/// <param name="message">Message to publish</param>
		/// <exception cref="InvalidOperationException">Message is not of the type wanted by the subscriber.</exception>
		public void Invoke(IMessage message)
		{
			string errMsg =
				string.Format("Message '{0}' is not assignable from '{1}'. Is the correct subscriber being invoked?",
				              _messageType.FullName,
				              message.GetType().FullName);

			if (!_messageType.IsAssignableFrom(message.GetType()))
				throw new InvalidOperationException(errMsg);


            try
            {
                _consumeMethod.Invoke(_subscriber, new object[] {message});
            }
            catch (TargetInvocationException err)
            {
                throw err.InnerException.FixStacktrace();
            }
		}
	}
}